package com.dsteiner.xelavos.client.controller;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Date;

import org.apache.commons.httpclient.util.DateUtil;

import com.dsteiner.widgets.client.DateUtilImpl;
import com.dsteiner.widgets.client.IJsUtil;
import com.dsteiner.widgets.client.JsUtil;
import com.dsteiner.widgets.client.StringUtil;
import com.dsteiner.widgets.client.Widgets.IHTML;
import com.dsteiner.widgets.client.Widgets.IImage;
import com.dsteiner.widgets.client.Widgets.ILazyWidget;
import com.dsteiner.widgets.client.Widgets.IWidget;
import com.dsteiner.widgets.client.Widgets.ImageExt;
import com.dsteiner.widgets.client.Widgets.WidgetListenerAdapter;
import com.dsteiner.widgets.client.controller.PopupOverlayController;
import com.dsteiner.widgets.client.domain.ModelObject;
import com.dsteiner.widgets.client.event.DataUpdateEvent;
import com.dsteiner.widgets.client.event.EventListener;
import com.dsteiner.widgets.client.event.EventManager;
import com.dsteiner.widgets.client.event.EventManagerImpl;
import com.dsteiner.widgets.client.event.MessageEvent;
import com.dsteiner.widgets.client.model.PopupOverlayModel;
import com.dsteiner.widgets.client.model.RateItModel;
import com.dsteiner.widgets.client.rpc.CrudResult;
import com.dsteiner.xelavos.client.SendMessageWidget.SendMessageModel;
import com.dsteiner.xelavos.client.constants.Constants;
import com.dsteiner.xelavos.client.domain.Commentable;
import com.dsteiner.xelavos.client.domain.HasImage;
import com.dsteiner.xelavos.client.domain.HasUser;
import com.dsteiner.xelavos.client.domain.Job;
import com.dsteiner.xelavos.client.domain.Leisure;
import com.dsteiner.xelavos.client.domain.Product;
import com.dsteiner.xelavos.client.domain.Rateable;
import com.dsteiner.xelavos.client.domain.Rating;
import com.dsteiner.xelavos.client.domain.Service;
import com.dsteiner.xelavos.client.domain.TrackComments;
import com.dsteiner.xelavos.client.domain.User;
import com.dsteiner.xelavos.client.event.CreateObjectEvent;
import com.dsteiner.xelavos.client.event.DeleteObjectEvent;
import com.dsteiner.xelavos.client.event.SendMessageEvent;
import com.dsteiner.xelavos.client.event.TrackCommentsEvent;
import com.dsteiner.xelavos.client.model.AuthenticationModel;
import com.dsteiner.xelavos.client.model.CommentsModel;
import com.dsteiner.xelavos.client.model.DisplayModel;
import com.dsteiner.xelavos.client.rpc.DataService;
import com.google.appengine.api.datastore.Text;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteEvent;
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteHandler;

public class DisplayController {

	private IImage image;
	private IHTML infoHtml;
	private DisplayModel displayModel;
	private IWidget noImageWidget;
	private IWidget rateItWidget;
	private RateItModel rateItModel;
	private IHTML ratingCountHtml;
	private AuthenticationModel authenticationModel;
	private PopupOverlayModel commentsPopupOverlayModel;
	private CommentsModel commentsModel;
	private IHTML commentsHtml;
	private IHTML userHtml;
	private PopupOverlayModel userPopupOverlayModel;
	private DisplayModel userDisplayModel;
	private IHTML xelavosImageHtml;
	private IHTML editLink;
	private IHTML removeLink;
	private ILazyWidget displayWidget;
	private AbstractImagePrototype trackCommentsPrototype;
	private AbstractImagePrototype trackCommentsSelectedPrototype;
	private Image trackCommentsIcon;
	private IImage sendMessageIcon;
	private IJsUtil jsUtil;
	private IHTML sendLinkHtml;
	private FormPanel formPanel;

	public void registerAuthenticationModel(AuthenticationModel authenticationModel) {
		this.authenticationModel = authenticationModel;
		authenticationModel.addPropertyChangeListener("user", new PropertyChangeListener(){
			public void propertyChange(PropertyChangeEvent evt) {
				updateUserInfo();
			}
		});
		authenticationModel.addPropertyChangeListener("trackComments", new PropertyChangeListener() {
			public void propertyChange(PropertyChangeEvent evt) {
				updateTrackCommentsIcon();
			}
		});
	}

	public void registerCommentsHtml(final IHTML commentsHtml) {
		this.commentsHtml = commentsHtml;
		commentsHtml.addClickHandler(new ClickHandler(){
			public void onClick(ClickEvent event) {
				jsUtil.trackEvent(Constants.CAT_COMMENT, Constants.ACT_CLICKED_CREATE_BUTTON);
				commentsModel.setRefObject(displayModel.getModelObject());
				PopupOverlayController.showAtCenter(commentsPopupOverlayModel);
			}
		});
	}

	public void registerCommentsModel(CommentsModel commentsModel) {
		this.commentsModel = commentsModel;
	}

	public void registerCommentsPopupOverlayModel(PopupOverlayModel commentsPopupOverlayModel) {
		this.commentsPopupOverlayModel = commentsPopupOverlayModel;
	}

	public void registerDisplayModel(DisplayModel displayModel) {
		this.displayModel = displayModel;
		displayModel.addPropertyChangeListener("modelObject", new PropertyChangeListener(){
			public void propertyChange(PropertyChangeEvent evt) {
				updateInfo();
				updateRatingInfo();
				updateUserInfo();
			}
		});
	}

	public void registerEditLink(IHTML editLink) {
		this.editLink = editLink;
		editLink.addClickHandler(new ClickHandler(){
			public void onClick(ClickEvent event) {
				EventManagerImpl.getInstance().fireEvent(new CreateObjectEvent(displayModel.getModelObject()));
			}
		});
	}

	public void registerEventManager(EventManager eventManager) {
		eventManager.registerListener(DataUpdateEvent.class, new EventListener<DataUpdateEvent>(){
			public void onEvent(DataUpdateEvent dataUpdateEvent) {
				ModelObject modelObject = displayModel.getModelObject();
				Object object = dataUpdateEvent.getObject();
				if(object.equals(modelObject)){
					displayModel.setModelObject((ModelObject) object);
					updateInfo();
					updateRatingInfo();
				}
			}
		});
		eventManager.registerListener(DeleteObjectEvent.class, new EventListener<DeleteObjectEvent>(){
			public void onEvent(DeleteObjectEvent event) {
				if(event.isConfirmed() && event.getModelObject().equals(displayModel.getModelObject()))
					displayWidget.setVisible(false);
			}
		});
	}
	
	public void registerImage(IImage image) {
		this.image = image;
	}

	public void registerInfoHtml(IHTML infoHtml) {
		this.infoHtml = infoHtml;
	}

	public void registerLazyWidget(final ILazyWidget displayWidget) {
		this.displayWidget = displayWidget;
		displayWidget.addWidgetListener(new WidgetListenerAdapter(){
			@Override
			public void onAttach(IWidget widget) {
				updateInfo();
				updateRatingInfo();
				updateUserInfo();
				updateTrackCommentsIcon();
			}
		});
	}


	public void registerNoImage(IWidget noImageWidget) {
		this.noImageWidget = noImageWidget;
	}

	public void registerRateItModel(final RateItModel rateItModel) {
		this.rateItModel = rateItModel;
		rateItModel.addPropertyChangeListener("clickedRating", new PropertyChangeListener(){
			public void propertyChange(PropertyChangeEvent evt) {
				doAddRating();
				jsUtil.trackEvent(Constants.CAT_RATING, Constants.ACT_CLICKED_ICON);
			}
		});
	}

	public void registerRateItWidget(IWidget rateItWidget) {
		this.rateItWidget = rateItWidget;
		rateItWidget.setVisible(false);
	}

	public void registerRatingCountHtml(IHTML ratingCountHtml) {
		this.ratingCountHtml = ratingCountHtml;
	}

	public void registerRemoveLink(IHTML removeLink) {
		this.removeLink = removeLink;
		removeLink.addClickHandler(new ClickHandler(){
			public void onClick(ClickEvent event) {
				EventManagerImpl.getInstance().fireEvent(new DeleteObjectEvent(displayModel.getModelObject()));
			}
		});
	}

	public void registerTrackCommentsIcon(Image trackCommentsIcon) {
		this.trackCommentsIcon = trackCommentsIcon;
		trackCommentsIcon.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				if(authenticationModel.getUser() == null){
					EventManagerImpl.getInstance().fireEvent(new MessageEvent("Necesita estar logeado para poder " +
							"estar notificado de nuevos comentarios"));
					return;
				}
				TrackComments trackComments = authenticationModel.getTrackComment(displayModel.getModelObject().getId());
				if(trackComments == null){
					trackComments = new TrackComments();
					trackComments.setObjectId(displayModel.getModelObject().getId());
					trackComments.setUserId(authenticationModel.getUser().getId());
				}
				EventManagerImpl.getInstance().fireEvent(new TrackCommentsEvent(trackComments));
			}
		});
	}

	public void registerTrackCommentsPrototype(AbstractImagePrototype trackCommentsPrototype) {
		this.trackCommentsPrototype = trackCommentsPrototype;
	}

	public void registerTrackCommentsSelectedPrototype(AbstractImagePrototype trackCommentsSelectedPrototype) {
		this.trackCommentsSelectedPrototype = trackCommentsSelectedPrototype;
	}

	public void registerUserDisplayModel(DisplayModel userDisplayModel) {
		this.userDisplayModel = userDisplayModel;
	}

	public void registerUserHtml(IHTML userHtml) {
		this.userHtml = userHtml;
		userHtml.addClickHandler(new ClickHandler(){
			public void onClick(ClickEvent event) {
				jsUtil.trackEvent(Constants.CAT_DISPLAY, Constants.ACT_CLICKED_USER_LINK);
				HasUser hasUserObj = (HasUser) displayModel.getModelObject();
				DataService.Util.getInstance().getObjectById(hasUserObj.getUserId(), 
						User.class.getName(), new AsyncCallback<CrudResult<ModelObject>>(){
					public void onFailure(Throwable caught) {
						caught.printStackTrace();
					}
					public void onSuccess(CrudResult result) {
						userDisplayModel.setModelObject(result.getModelObject());
						PopupOverlayController.showAtCenter(userPopupOverlayModel);
					}
				});
			}
		});
	}

	public void registerUserPopupOverlayModel(PopupOverlayModel userPopupOverlayModel) {
		this.userPopupOverlayModel = userPopupOverlayModel;
	}

	public void registerXelavosImageHtml(IHTML xelavosImageHtml) {
		this.xelavosImageHtml = xelavosImageHtml;
	}

	private void doAddRating() {
		final Integer rating = rateItModel.getRating();
		if(rating == null)
			return;
		
		final ModelObject modelObject = displayModel.getModelObject();
		User user = authenticationModel.getUser();
		if(user == null){
			JsUtil.getInstance().showAlert("necesita estar logeado para clasificar");
			rateItModel.setRating(((Rateable)modelObject).getRating());
			return;
		}
		
		boolean alreadyRated = authenticationModel.isAlreadyRated(modelObject.getId());
		if(alreadyRated){
			rateItModel.setMessage(Constants.MSG_OBJ_ALREADY_RATED);
			rateItModel.setRating(((Rateable)modelObject).getRating());
			return;
		}
		
		final Rating ratingObj = new Rating();
		ratingObj.setUserId(user.getId());
		ratingObj.setRating(rating);
		ratingObj.setRefId(modelObject.getId());
		ratingObj.setRefClass(modelObject.getClass().getName());
		ratingObj.setRegisteredDate(new Date());
		authenticationModel.addRating(ratingObj);
		rateItModel.setMessage(null);
		((Rateable)modelObject).addRating(rating);
		EventManagerImpl.getInstance().fireEvent(new DataUpdateEvent(modelObject));
		DataService.Util.getInstance().createModelObject(ratingObj, new AsyncCallback<CrudResult<ModelObject>>(){
			public void onFailure(Throwable caught) {
				caught.printStackTrace();
			}
			public void onSuccess(CrudResult result) {
				if(Constants.MSG_OBJ_ALREADY_RATED.equals(result.getError())){
					authenticationModel.removeRating(ratingObj);
					((Rateable)modelObject).removeRating(rating);
					rateItModel.setMessage(Constants.MSG_OBJ_ALREADY_RATED);
					rateItModel.setRating(((Rateable)modelObject).getRating());
					EventManagerImpl.getInstance().fireEvent(new DataUpdateEvent(modelObject));
				}
			}
		});
	}

	private String format(ModelObject modelObject) {
		StringBuffer sb = new StringBuffer();
		if(modelObject instanceof Product){
			Product product = (Product) modelObject;
			sb.append("<strong>Nombre</strong> : "+product.getName()).append("<br/>");
			if(product.getPrice() != null)
				sb.append("<strong>Precio</strong> : "+JsUtil.getInstance().format(product.getPrice(), 
					Constants.NUMBER_FORMAT_QUETZAL)).append("<br/>");
			if(product.getShop() != null && !product.getShop().equals(""))
				sb.append("<strong>Tienda</strong> : "+product.getShop()).append("<br/>");
			if(product.getBrand() != null)
				sb.append("<strong>Marca</strong> : "+product.getBrand()).append("<br/>");
			if(product.getModel() != null)
				sb.append("<strong>Modelo</strong> : "+product.getModel()).append("<br/>");
			if(product.getValidUntilDate() != null)
				sb.append("<strong>Valido hasta</strong> : "+DateUtilImpl.getInstance().formatDateSpanish(product.getValidUntilDate())).append("<br/>");
			String contact = product.getContact();
			if(!StringUtil.isEmpty(contact)){
				sb.append("<strong>Contacto</strong> : "+StringUtil.toHtml(contact)).append("<br/>");
			}
			Text description = product.getDescription();
			if(description != null && !StringUtil.isEmpty(description.toString())){
				sb.append("<strong>Descripcion</strong> : "+description.getValue()).append("<br/>");
			}
		}
		else if(modelObject instanceof Job){
			Job job= (Job) modelObject;
			sb.append("<strong>Nombre</strong> : "+job.getName()).append("<br/>");
			if(!StringUtil.isEmpty(job.getEmployer()))
				sb.append("<strong>Empleador</strong> : "+job.getEmployer()).append("<br/>");
			String contact = job.getContact();
			sb.append("<strong>Contacto</strong>: "+StringUtil.toHtml(contact)).append("<br/>");
			if(job.getValidUntilDate() != null)
				sb.append("<strong>valido hasta</strong> : "+DateUtilImpl.getInstance().formatDateSpanish(job.getValidUntilDate())).append("<br/>");
			sb.append("<strong>Descripcion</strong> : "+job.getDescription().getValue());
		}
		else if(modelObject instanceof Leisure){
			Leisure leisure= (Leisure) modelObject;
			sb.append("<strong>Nombre</strong> : "+leisure.getName()).append("<br/>");
			sb.append("<strong>Lugar</strong> : "+leisure.getPlace()).append("<br/>");
			if(leisure.getEventDate() != null){
				Date parseDate = DateUtilImpl.parseDate(leisure.getEventDate(), DateUtilImpl.formatterSpanish);
				String formatDate = DateUtilImpl.formatDate(DateUtilImpl.formatterSpanishLong, parseDate);
				sb.append("<strong>Fecha</strong> : ").append(formatDate);
				if(leisure.getEventDateUntil() != null){
					Date parseDateUntil = DateUtilImpl.parseDate(leisure.getEventDateUntil(), DateUtilImpl.formatterSpanish);
					String formatDateUntil = DateUtilImpl.formatDate(DateUtilImpl.formatterSpanishLong, parseDateUntil);
					sb.append(" hasta ").append(formatDateUntil);
				}
				sb.append("<br/>");
			}
			if(leisure.getEventHour() != null){
				sb.append("<strong>Hora</strong> : "+
						leisure.getEventHour()).append("<br/>");
			}
			String contact = leisure.getContact();
			if(contact != null){
				sb.append("<strong>Contacto</strong> : ").append(StringUtil.toHtml(contact)).append("<br/>");
			}
			sb.append("<strong>Descripcion</strong> : "+leisure.getDescription().getValue());
		}
		else if(modelObject instanceof Service){
			Service service = (Service) modelObject;
			sb.append("<strong>Tipo</strong> : "+service.getType()).append("<br/>");
			if(service.getName() != null && !service.getName().equals(""))
				sb.append("<strong>Nombre</strong> : "+service.getName()).append("<br/>");
			if(service.getPlace() != null && !service.getPlace().equals(""))
				sb.append("<strong>Lugar</strong> : "+service.getPlace()).append("<br/>");
			String contact = service.getContact();
			sb.append("<strong>Contacto</strong> : ").append(StringUtil.toHtml(contact)).append("<br/>");
			sb.append("<strong>Descripcion</strong> : "+service.getDescription().getValue());
		}
		else if(modelObject instanceof User){
			User user= (User) modelObject;
			String joinDatePassed = DateUtilImpl.formatDatePassedSince(user.getJoinDate());
			
			sb.append("<strong>").append(user.toString()).append("</strong><br/>");
			sb.append("con <strong>").append(user.getAge()).append(" años </strong><br/>");
			sb.append("miembro desde <strong>").append(joinDatePassed).append("</strong>");
			if(user.getEntriesCount() > 0)
				sb.append("<br/>con <strong>").append(user.getEntriesCount()).append(" registros</strong>");
		}
		String sendLinkUrl = Constants.Util.getModelObjectUrl(modelObject);
		String sendLinkMsg = "Hay una publicacion de "+modelObject.toString()
		+" en Xelavos%0Apara verlo visite el siguiente link%0A%0A"+sendLinkUrl;
		sendLinkHtml.setHTML("<a href=\"mailto:?subject=Publicacion en Xelavos&body="+sendLinkMsg+"\">mandar link</a>");
		String string = sb.toString();
		return string;
	}

	private void updateInfo(){
		ModelObject modelObject = displayModel.getModelObject();
		if(modelObject == null)
			return;
		
		String imageId = null;
		if(modelObject instanceof HasImage)
			imageId = ((HasImage)modelObject).getImageId();
		
		image.setVisible(imageId != null);
		noImageWidget.setVisible(imageId == null);
		xelavosImageHtml.setVisible(Constants.Util.isXelavosImage(imageId));
		image.setUrl(Constants.Util.getImageUrl(imageId));
		
		String info = format(modelObject);
		infoHtml.setHTML(info);
	}

	private void updateRatingInfo() {
		ModelObject modelObject = displayModel.getModelObject();
		boolean rateable = modelObject instanceof Rateable;
		rateItWidget.setVisible(rateable);
		ratingCountHtml.setVisible(rateable);
		if(rateable){
			Rateable rateableObj = (Rateable) modelObject;
			int ratingCount = rateableObj.getRatingCount();
			ratingCountHtml.setHTML("("+ratingCount+")");
			rateItModel.setRating(rateableObj.getRating());
		}
		boolean commentable = modelObject instanceof Commentable;
		commentsHtml.setVisible(commentable);
		if(commentable){
			Commentable commentableObj = (Commentable) modelObject;
			Integer commentCount = commentableObj.getCommentCount();
			commentsHtml.setHTML("<a href=\"javascript:void(0);\">"+
					(commentCount != null && commentCount > 0 ? 
							(commentCount + (commentCount == 1 ? " comentario" : " comentarios")) : 
						"escriba un comentario")+"</a>");
		}
	}

	private void updateTrackCommentsIcon() {
		TrackComments trackComments = authenticationModel.getTrackComment(displayModel.getModelObject().getId());
		if(trackComments == null)
			trackCommentsPrototype.applyTo(trackCommentsIcon);
		else
			trackCommentsSelectedPrototype.applyTo(trackCommentsIcon);
		trackCommentsIcon.setTitle(trackComments == null ? "recibir correos con nuevos comentarios" : 
			"ya no seguir nuevos comentarios");
		trackCommentsIcon.setVisible(displayModel.getModelObject() instanceof Commentable);
	}

	private void updateUserInfo() {
		ModelObject modelObject = displayModel.getModelObject();
		User user = authenticationModel.getUser();
		boolean hasUser = modelObject instanceof HasUser;
		userHtml.setVisible(hasUser);
		sendMessageIcon.setVisible(user != null && modelObject instanceof User && !user.getId().equals(modelObject.getId()));
		boolean isFromUser = hasUser && user != null && ((HasUser)modelObject).getUserId().equals(user.getId());
		editLink.setVisible(isFromUser);
		removeLink.setVisible(isFromUser);
		if(hasUser){   
			String firstName = ((HasUser)modelObject).getUserFirstName();
			userHtml.setHTML("ingresado por <a href=\"javascript:void(0);\">" + firstName
					+"</a> <span class=\"registeredDateHtml\">"+DateUtilImpl.formatDatePassedSince(modelObject.getRegisteredDate())+"</span>");
		}
	}

	public void registerSendMessageIcon(IImage sendMessageIcon) {
		this.sendMessageIcon = sendMessageIcon;
		sendMessageIcon.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				EventManagerImpl.getInstance().fireEvent(new SendMessageEvent(
						displayModel.getModelObject().getId(), ((User)displayModel.getModelObject()).getFirstName()));
			}
		});
	}

	public void registerJsUtil(IJsUtil jsUtil) {
		this.jsUtil = jsUtil;
	}

	public void registerSendLinkHtml(IHTML sendLinkHtml) {
		this.sendLinkHtml = sendLinkHtml;
		sendLinkHtml.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				formPanel.submit();
			}
		});
	}

	public void registerFormPanel(FormPanel formPanel) {
		this.formPanel = formPanel;
		formPanel.addSubmitCompleteHandler(new SubmitCompleteHandler(){
			public void onSubmitComplete(SubmitCompleteEvent event) {
				String results = event.getResults();
				System.out.println(results);
			}
		});
	}
}
